//
//  NetworkManager.m
//  MapLink
//
//  Created by Power User on 5/28/11.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import "NetworkManager.h"
#import <SystemConfiguration/SystemConfiguration.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <ifaddrs.h>

#import "GroupEntity.h"
#import "UserLocationEntity.h"
#import "NotificationEntity.h"

#import "JSON/JSON.h"

@implementation NetworkManager

SCNetworkConnectionFlags connectionFlags;

@synthesize buffer, serverString;

- (void) responseCreateGroup:(NSDictionary *)response {
	NSLog(@"response: CreateGroup");
}

- (void) responseJoinGroup:(NSDictionary *)response {
	NSLog(@"response: JoinGroup");
}

- (void) responseUnjoinGroup:(NSDictionary *)response {
	NSLog(@"response: UnjoinGroup");
}

- (void) responseUpdateLocation:(NSDictionary *)response {
	NSLog(@"response: UpdateLocation");
}

- (void) responseUpdateMeetingPoint:(NSDictionary *)response {
	NSLog(@"response: UpdateMeetingPoint");
}

- (void) responseSendNotification:(NSDictionary *)response {
	NSLog(@"response: SendNotification");
}

- (void) responseRetrieveNotification:(NSDictionary *)response {
	NSLog(@"response: RetrieveNotification");
}

- (void) processResponse:(NSMutableData	*)data{
	NSMutableString *jsonString = [[NSMutableString alloc] initWithBytes:[data mutableBytes]
																  length:[data length]
																encoding:NSUTF8StringEncoding];
	NSDictionary *response = [jsonString JSONValue];
	NSString *result = [response objectForKey:@"result"];
	NSString *action = [response objectForKey:@"action"];
	NSLog(@"JSON: %@ %@",jsonString,action);
	
	if ([result isEqualToString:@"Y"]) {
		// Action successed
		if ([action isEqualToString:@"create-group"]) {
			// Create group
			[self responseCreateGroup:response];
		} else if ([action isEqualToString:@"join-group"]) {
			// Join group
			[self responseJoinGroup:response];
		} else if ([action isEqualToString:@"unjoin-group"]) {
			// Unjoin group
			[self responseUnjoinGroup:response];
		} else if ([action isEqualToString:@"update-location"]) {
			// Update location
			[self responseUpdateLocation:response];
		} else if ([action isEqualToString:@"update-meeting-point"]) {
			// Update meeting point
			[self responseUpdateMeetingPoint:response];
		} else if ([action isEqualToString:@"send-notification"]) {
			// Send notification
			[self responseSendNotification:response];
		} else if ([action isEqualToString:@"retrieve-notification"]) {
			// Retrieve notification
			[self responseRetrieveNotification:response];
		} else {
			// Action is not implemented!
			NSLog(@"Action is not implemented!");
		}
	} else {
		// Action failed
	}

	[jsonString release];
}

/* START: NetworkManager API */
- (id) initWithServerString:(NSString *) str {
	[super init];
	self.serverString = str;
	return self;
}

- (void) dealloc {
	[buffer release];
	[super dealloc];
}

- (void) performSynchronousRequest:(NSString *)strRequest {
	if (![NetworkManager networkAvailable]) {
		return;
	}
	NSString *urlString = [strRequest stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
	NSURL *url = [NSURL URLWithString:urlString];
	
	if (!url) {
		// error in creating NSURL object
	} else {
		NSLog(@"Sync URL:%@",urlString);
		NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
		if (!req) {
			// error in creating Request object
		} else {
			NSURLResponse *response;
			NSError *error;
			NSData *result = [NSURLConnection sendSynchronousRequest:req returningResponse:&response error:&error];
			
			[self processResponse:[NSMutableData dataWithData:result]];
		}

	}

}

- (void) performASynchronousRequest:(NSString *)strRequest {
	if (![NetworkManager networkAvailable]) {
		return;
	}
	NSString *urlString = [strRequest stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
	NSURL *url = [NSURL URLWithString:urlString];
	
	if(!url) {
		// error in creating NSURL object
	} else {
		NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
		if (!req) {
			// error in creating Request object
		} else {
			[req setHTTPMethod:@"GET"];
			conn = [[NSURLConnection alloc] initWithRequest:req delegate:self];
			if (conn) {
				self.buffer = [NSMutableData data]; 
			}
		}
	}	
}


- (void) createGroup:(GroupEntity *)group byUser:(UserLocationEntity *)user {
	NSString *urlString = [NSString stringWithFormat:@"%@/group.php?action=%@&device_id=%@&name=%@&group=%@&password=%@",
						  self.serverString,
						   @"create",
						  user.deviceId,
						  user.userName,
						  group.groupName,
						  group.groupCode
						   ];
	[self performSynchronousRequest:urlString];
}

- (void) joinGroup:(GroupEntity *)group withUser:(UserLocationEntity *)user {
	NSString *urlString = [NSString stringWithFormat:@"%@/group.php?action=%@&device_id=%@&name=%@&group=%@&password=%@",
						   self.serverString,
						   @"join",
						   user.deviceId,
						   user.userName,
						   group.groupName,
						   group.groupCode
						   ];
	[self performSynchronousRequest:urlString];
}

- (void) unjoinGroup:(GroupEntity *)group withUser:(UserLocationEntity *)user {
	NSString *urlString = [NSString stringWithFormat:@"%@/group.php?action=%@&device_id=%@&name=%@&group=%@",
						   self.serverString,
						   @"unjoin",
						   user.deviceId,
						   user.userName,
						   group.groupName
						   ];
	[self performSynchronousRequest:urlString];
}

- (void) updateLocation:(UserLocationEntity *)user forGroup:(GroupEntity *)group {
	NSString *urlString = [NSString stringWithFormat:@"%@/update.php?device_id=%@&name=%@&group=%@&latitude=%@&longitude=%@",
						   self.serverString,
						   user.deviceId,
						   user.userName,
						   group.groupName,
						   user.locationX,
						   user.locationY
						   ];
	[self performASynchronousRequest:urlString];
}

- (void) updateMeetingPointForGroup:(GroupEntity *)group byUser:(UserLocationEntity *)user {
	NSString *urlString = [NSString stringWithFormat:@"%@/meetingpoint.php?device_id=%@&group=%@&latitude=%@&longitude=%@",
						   self.serverString,
						   user.deviceId,
						   group.groupName,
						   group.meetingPointLocationX,
						   group.meetingPointLocationY
						   ];
	[self performSynchronousRequest:urlString];
}

- (void) sendNotification:(NotificationEntity *)notification {
	NSString *urlString = [NSString stringWithFormat:@"%@/postmessage.php?device_id=%@&name=%@&group=%@&latitude=%@&longitude=%@&message=%@",
						   self.serverString,
						   notification.deviceId,
						   notification.userName,
						   notification.groupName,
						   notification.locationX,
						   notification.locationY,
						   notification.notificationMsg
						   ];
	[self performSynchronousRequest:urlString];
	
}

- (void) retrieveNotification:(UserLocationEntity *)user {
	NSString *urlString = [NSString stringWithFormat:@"%@/getmessage.php?device_id=%@&name=%@&group=%@",
						   self.serverString,
						   user.deviceId,
						   user.userName,
						   user.groupName
						   ];
	[self performASynchronousRequest:urlString];
}
/* END: NetworkManager API */


/* START: Connection delegate */
-(void) connection:(NSURLConnection *) connection didReceiveResponse:(NSURLResponse *) response {
	[buffer setLength:0];
}
-(void) connection:(NSURLConnection *) connection didReceiveData:(NSData *) data {
	[buffer appendData:data];
}
-(void) connection:(NSURLConnection *) connection didFailWithError:(NSError *) error {
	[buffer release];
	[connection release];
}
-(void) connectionDidFinishLoading:(NSURLConnection *) connection {
	NSLog(@"Done with bytes %d", [buffer length]);
	[self processResponse:buffer];
	[connection release];
}
/* END: Connection delegate */

/* START: Network Reachability */
+ (BOOL) connectedToNetwork {
	struct sockaddr_in zeroAddress;
	bzero(&zeroAddress, sizeof(zeroAddress));
	zeroAddress.sin_len = sizeof(zeroAddress);
	zeroAddress.sin_family = AF_INET;
	
	SCNetworkReachabilityRef defaultRouteReachability = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr*)&zeroAddress);
	SCNetworkReachabilityFlags flags;
	
	BOOL didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags);
	
	CFRelease(defaultRouteReachability);
	
	if (!didRetrieveFlags) {
		return NO;
	}
	
	BOOL isReachable = flags & kSCNetworkFlagsReachable;
	BOOL needsConnection = flags & kSCNetworkFlagsConnectionRequired;
	return (isReachable && !needsConnection) ? YES : NO;
}

+ (BOOL) networkAvailable {
	[self pingReachabilityInternal];
	BOOL isReachable = ((connectionFlags & kSCNetworkFlagsReachable) !=0 );
	BOOL needsConnection = ((connectionFlags & kSCNetworkFlagsConnectionRequired) != 0);
	return (isReachable && !needsConnection) ? YES : NO;
}

+ (BOOL) activeWWAN {
	if (![self networkAvailable]) return NO;
	
	return ((connectionFlags & kSCNetworkReachabilityFlagsIsWWAN) != 0);
}

+ (BOOL) activeWLAN {
	return ([NetworkManager localWiFiIPAddress] != nil);
}

+ (NSString *) localWiFiIPAddress {
	BOOL success;
	struct ifaddrs *addrs;
	const struct ifaddrs * cursor;
	
	success = getifaddrs(&addrs) == 0;
	
	if (success) {
		cursor = addrs;
		while (cursor != NULL) {
			if (cursor->ifa_addr->sa_family == AF_INET && (cursor->ifa_flags & IFF_LOOPBACK) == 0) {
				NSString *name = [NSString stringWithUTF8String:cursor->ifa_name];
				if ([name isEqualToString:@"en0"]) {
					return [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *) cursor->ifa_addr)->sin_addr)];
				}
			}
			cursor = cursor->ifa_next;
		}
		freeifaddrs(addrs);
	}
	return nil;
}

+ (void) pingReachabilityInternal {
	BOOL ignoresAdHocWifi = NO;
	struct sockaddr_in ipAddress;
	bzero(&ipAddress, sizeof(ipAddress));
	ipAddress.sin_len = sizeof(ipAddress);
	ipAddress.sin_family = AF_INET;
	ipAddress.sin_addr.s_addr = htonl(ignoresAdHocWifi ? INADDR_ANY : IN_LINKLOCALNETNUM);
	
	SCNetworkReachabilityRef defaultRouteReachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (struct sockaddr *)&ipAddress);
	BOOL didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &connectionFlags);
	CFRelease(defaultRouteReachability);
	
	if (!didRetrieveFlags) {
		NSLog(@"Error. Could not recover flags");
	}
}
/* END: Network Reachability */

@end
